package net.oschina.bilbodai.common.beanutil.bean.code;

import net.oschina.bilbodai.common.beanutil.bean.BeanDumpUtil;
import net.oschina.bilbodai.common.beanutil.bean.BeanInfo;
import net.oschina.bilbodai.common.beanutil.bean.core.PropertyDumpInfo;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;

import static net.oschina.bilbodai.common.beanutil.bean.BeanInfo.LOCAL_INDEX_CAST_TARGET;
import static net.oschina.bilbodai.common.beanutil.bean.BeanInfo.LOCAL_INDEX_COLLECT_CREATE_ACCESSOR;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;

/**
 * @author daimao  Date: 16/5/30 Time: 下午3:34
 * @version $Id$
 */
public abstract class GetterSetterCodeApplier implements IPropertyCodeApplier {

    protected final PropertyDumpInfo dumpInfo;

    public GetterSetterCodeApplier(PropertyDumpInfo dumpInfo) {
        this.dumpInfo = dumpInfo;
    }

    protected void loadTargetCastPropertyOntoStack(MethodVisitor mv) {
        loadTargetCastPropertyOntoStack0(mv);
    }

    protected void loadSrcCastPropertyOntoStack(MethodVisitor mv) {
        loadSrcCastPropertyOntoStack0(mv);
    }

    protected final void loadSrcCastOntoStack0(MethodVisitor mv) {
        mv.visitVarInsn(ALOAD, BeanInfo.LOCAL_INDEX_CAST_SRC);
    }

    protected final void loadTargetCastOntoStack0(MethodVisitor mv) {
        mv.visitVarInsn(ALOAD, LOCAL_INDEX_CAST_TARGET);
    }

    protected final void loadTargetCastPropertyOntoStack0(MethodVisitor mv) {
        loadTargetCastOntoStack0(mv);
        mv.visitMethodInsn(INVOKEVIRTUAL,
                dumpInfo.getTargetOwnerInternalName(),
                dumpInfo.getTargetGetterName(),
                dumpInfo.getTargetGetterDescriptor(), false);
    }

    protected final void loadSrcCastPropertyOntoStack0(MethodVisitor mv) {
        loadSrcCastOntoStack0(mv);
        mv.visitMethodInsn(INVOKEVIRTUAL,
                dumpInfo.getSrcOwnerInternalName(),
                dumpInfo.getSrcGetterName(),
                dumpInfo.getSrcGetterDescriptor(), false);
    }

    protected final void invokeTargetPropertySetter0(MethodVisitor mv) {
        mv.visitMethodInsn(INVOKEVIRTUAL,   // target setter call
                dumpInfo.getTargetOwnerInternalName(),
                dumpInfo.getTargetSetterName(),
                dumpInfo.getTargetSetterDescriptor(),
                false);
    }

    protected final void invokeCreateBeanAccessorAndStoreLocal(MethodVisitor mv, Class<?> srcType, Class<?> targetType) {
        mv.visitLdcInsn(Type.getType(srcType)); // create accessor
        mv.visitLdcInsn(Type.getType(targetType));
        mv.visitLdcInsn(dumpInfo.getProfile());
        mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(BeanDumpUtil.class), BeanDumpUtil.CREATE_ACCESSOR_METHOD.getName(), Type.getMethodDescriptor(BeanDumpUtil.CREATE_ACCESSOR_METHOD), false);
        mv.visitVarInsn(ASTORE, LOCAL_INDEX_COLLECT_CREATE_ACCESSOR);//7
    }

    protected final void localAccessorOntoStack(MethodVisitor mv) {
        mv.visitVarInsn(ALOAD, LOCAL_INDEX_COLLECT_CREATE_ACCESSOR);
    }

    protected void applySortInsIfPossible(MethodVisitor mv) {
        if (dumpInfo.isSorted()) {
            loadTargetCastPropertyOntoStack(mv);
            mv.visitMethodInsn(INVOKESTATIC, "java/util/Collections", "sort", "(Ljava/util/List;)V", false);
        }
    }
}